package cn.org.rookie.jeesdp.mapper.sql;

import cn.org.rookie.jeesdp.mapper.entity.ColumnInfo;
import cn.org.rookie.jeesdp.mapper.entity.TableInfo;
import cn.org.rookie.jeesdp.mapper.sql.condition.Condition;

import java.util.Collection;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

public class Sql {
    private static final ConcurrentHashMap<String, String> CONTEXT = new ConcurrentHashMap<>();
    private static final String INSERT_INTO = "INSERT INTO %s (%s) VALUES (%s)";
    private static final String DELETE_FROM = "DELETE FROM ";
    private static final String UPDATE = "UPDATE %s <set> %s </set>";
    private static final String SELECT = "SELECT %s FROM %s";

    private final TableInfo table;
    private String sql;

    private Sql(TableInfo table) {
        this.table = table;
    }

    public static Sql of(TableInfo tableInfo) {
        return new Sql(tableInfo);
    }

    private static String ifScript(String sql, String field) {
        return String.format("<if test=\"%s != null\">%s</if>", field, sql);
    }

    private static String sharp(String field) {
        return String.format("#{%s}", field);
    }

    private static String join(Collection<String> source) {
        StringJoiner joiner = new StringJoiner(",", " ORDER BY ", "");
        for (String s : source) {
            joiner.add(s);
        }
        return joiner.toString();
    }


    public Sql insert() {
        sql = CONTEXT.computeIfAbsent(table.getName() + ".insert", k -> String.format(INSERT_INTO, table.getName(), String.join(",", table.getColumnNames()), "#{" + String.join("}, #{", table.getFieldNames()) + "}"));
        return this;
    }

    public Sql delete() {
        sql = CONTEXT.computeIfAbsent(table.getName() + ".delete", k -> DELETE_FROM + table.getName());
        return this;
    }

    public Sql update() {
        StringBuilder sb = new StringBuilder();
        for (ColumnInfo column : table.getColumns()) {
            if (!column.getName().equals(table.getPrimaryKey().getName())) {
                sb.append(ifScript(String.format("%s = #{param.%s},", column.getName(), column.getFieldName()), "param." + column.getFieldName()));
            }
        }
        sql = CONTEXT.computeIfAbsent(table.getName() + ".update", k -> String.format(table.getName(), sb));
        return this;
    }

    public Sql select() {
        sql = CONTEXT.computeIfAbsent(table.getName() + ".select", k -> String.format(SELECT, String.join(",", table.getColumnNames()), table.getName()));
        return this;
    }

    public Sql byPrimaryKey() {
        sql += String.format(" WHERE %s = #{%s}", table.getPrimaryKey().getName(), "param." + table.getPrimaryKey().getName());
        return this;
    }

    public Sql byPrimaryKey(Object param) {
        where(Wrapper.init().equal(table.getPrimaryKey().getName(), param));
        return this;
    }

    public Sql where(Wrapper wrapper) {
        StringBuilder sb = new StringBuilder();
        if (wrapper != null) {
            List<Condition> conditionList = wrapper.getConditions();
            for (int i = 0; i < conditionList.size(); i++) {
                Condition condition = conditionList.get(i);
                if (i != 0) {
                    sb.append(Boolean.TRUE.equals(condition.isAnd()) ? " AND " : " OR ");
                }
                sb.append(condition.render());
            }
            List<String> order = wrapper.getOrder();
            if (!sb.isEmpty()) {
                if (sql.contains("WHERE")) {
                    sql += (" AND " + sb);
                } else {
                    sql += (" WHERE " + sb);
                }
            }

            if (!order.isEmpty()) {
                sql += join(order);
            }
        }
        return this;
    }

    public String build() {
        return "<script>\n" + sql + "\n</script>";
    }

}
